package org.itboys.pay.weixin;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.itboys.commons.CommonConstants;
import org.itboys.pay.constant.PayConstants;
import org.itboys.pay.tools.XMLUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;

/** 
 * @author 作者 huml 
 * @version 创建时间：2016-4-1 上午11:16:51 
 * 微信支付工具类 
 */
public class WeixinPayUtils {
	
	private static  Logger logger = LoggerFactory.getLogger(WeixinPayUtils.class);
	
	/**
	 * 微信支付前的统一下单和签名    返回的实体中已经含有所需要的参数
	 * @param appid  微信appid
	 * @param mchId  微信商户号
	 * @param openid  openid仅当trade_type=JSAPI时，此参数必传，用户在商户appid下的唯一标识
	 * @param outTradeNo  商户系统内部的订单号,32个字符内、可包含字母 必须
	 * @param spbillCreateIp   APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP 必须
	 * @param tradeType 交易类型 trade_type=JSAPI表示H5页面发起调用 必须  JSAPI APP NATIVE(扫码支付)
	 * @param body 商品或支付单简要描述 必须
	 * @param totalFee  订单总金额,只能为整数,单位为分 必须
	 * @param notifyUrl  异步回调链接
	 * @param key  支付秘钥
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static WeixinPaySignTool paySign(String appid, String mchId,
			String openid, String outTradeNo,
			String spbillCreateIp, String tradeType, String body,
			String totalFee, String notifyUrl, String productId, String key) throws Exception{
		//创建统一下单实体
		//2017年6月19日，删除product参数
		Unifiedorder unifiedorder = new Unifiedorder(appid, mchId, openid, outTradeNo, spbillCreateIp, tradeType, body, totalFee, notifyUrl, key);
		String param = unifiedorder.getXML();//获得统一下单的xml参数
		logger.info("*****param******"+param);
		String result = WeixinPayUtils.execute(param, Unifiedorder.URL);//发起统一下单访问并获得返回结果
		WeixinPaySignTool tool = null;
		if(StringUtils.isNotBlank(result)){
			Map<String, Object> map = XMLUtil.doXMLParse(result);
			String returnCode = (String)map.get(PayConstants.WeixinPay.RETURN_CODE);
			String resultCode = (String)map.get(PayConstants.WeixinPay.RESULT_CODE);
			if(returnCode.toLowerCase().equals(CommonConstants.SUCCESS) && resultCode.toLowerCase().equals(CommonConstants.SUCCESS)){
				String prepayId = (String)map.get("prepay_id");
				tool = new WeixinPaySignTool(appid, prepayId, key);
				if(tradeType.equals(Unifiedorder.TRADE_TYPE_SCAN)){
					tool.setCodeUrl((String)map.get("code_url"));
				}
				logger.info("**********WeixinPaySignTool*************"+tool.toString());
				
			}else {//统一下单失败
				String returnMsg = (String)map.get(PayConstants.WeixinPay.RETURN_MSG);
				String errCode = (String)map.get(PayConstants.WeixinPay.ERR_CODE);
				String errCodeDes = (String)map.get(PayConstants.WeixinPay.ERR_CODE_DES);
				logger.info("***********"+returnMsg+"**********"+errCode+"************"+errCodeDes);
				throw new RuntimeException("***********"+returnMsg+"**********"+errCode+"************"+errCodeDes);
			}
		} else {
			throw new RuntimeException("***********发起访问错误**********");
		}
		return tool;
	}

	/*public static void main(String[] args){
		try {
			String weixinPaySignTool = nativePay("wxe00e3899f4633ecd", "1403007302", "11111111111", "127.0.0.1", "test",
					"1", "http://admin.fuzion.com", "1", "123456789000000zsmzsmzsmzsm00000", "C:\\Users\\Administrator\\Desktop");
			logger.info("*******************" + JSON.toJSONString(weixinPaySignTool));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}*/

	/**
	 * 微信扫码支付
	 * @param appid
	 * @param mchId
	 * @param outTradeNo
	 * @param spbillCreateIp  调用微信支付API的机器IP
	 * @param body
	 * @param totalFee
	 * @param notifyUrl
	 * @param productId  产品id
	 * @param key
	 * @param path  二维码存放路径
	 * @return
	 * @throws Exception
	 */
/*	public static String nativePay(String appid, String mchId, String outTradeNo,
								 String spbillCreateIp, String body,
								 String totalFee, String notifyUrl, String productId, String key, String path) throws Exception{
		WeixinPaySignTool weixinPaySignTool = paySign(appid, mchId, null, outTradeNo, spbillCreateIp, Unifiedorder.TRADE_TYPE_SCAN, body,
				totalFee, notifyUrl, productId, key);
		return CodeUtils.createTwoDimensionalCode(weixinPaySignTool.getCodeUrl(), outTradeNo, path, 500, 500);
	}*/

	/**
	 * APP微信支付
	 * @param appid  app应用号
	 * @param mchId  商户id
	 * @param outTradeNo  商户订单号
	 * @param spbillCreateIp  ip地址
	 * @param body
	 * @param totalFee
	 * @param notifyUrl
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static AppPaySignTool appPay(String appid, String mchId,
							  String outTradeNo, String spbillCreateIp,
							  String body, String totalFee, String notifyUrl, String key) throws Exception{
		WeixinPaySignTool weixinPaySignTool = paySign(appid, mchId, StringUtils.EMPTY, outTradeNo, spbillCreateIp,
				Unifiedorder.TRADE_TYPE_APP, body, totalFee, notifyUrl, null, key);

		return new AppPaySignTool(appid, mchId, weixinPaySignTool.getPackage_().replace("prepay_id=", StringUtils.EMPTY), key);
	}


	/**
	 * 执行url访问
	 * @param param  参数
	 * @param url  链接
	 * @return
	 */
	public static String execute(String param, String url) throws Exception {
		URL urll = new URL(url);
		HttpURLConnection connection = (HttpURLConnection) urll.openConnection();

		connection.setDoOutput(true);
		connection.setDoInput(true);
		connection.setUseCaches(false);
		connection.setInstanceFollowRedirects(true);
		connection.setRequestMethod("POST");
		connection.setRequestProperty("Accept", "application/string");
		connection.setRequestProperty("Content-Type", "application/xml");
		connection.connect();
		OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");

		out.append(param.toString());
		out.flush();
		out.close();

		int length = connection.getContentLength();
		InputStream is = connection.getInputStream();
		String result = StringUtils.EMPTY;
		if (length != -1) {
			byte[] data = new byte[length];
			byte[] temp = new byte[512];
			int readLen = 0;
			int destPos = 0;
			while ((readLen = is.read(temp)) > 0) {
				System.arraycopy(temp, 0, data, destPos, readLen);
				destPos += readLen;
			}
			result = new String(data, "UTF-8");
		}
		return result;
	}

	/**
	 *网页微信支付 。2017年8月23日江雄新增
	 * @param appid  服务好APPID
	 * @param mchId  商户id
	 * @param openid
	 * @param outTradeNo
	 * @param spbillCreateIp
	 * @param body
	 * @param totalFee
	 * @param notifyUrl
	 * @param key 微信支付API
	 * @return
	 * @throws Exception
	 */
	public static JsPaySignTool jsPay(String appid, String mchId,String openid,
										String outTradeNo, String spbillCreateIp,
										String body, String totalFee, String notifyUrl, String key) throws Exception{
		WeixinPaySignTool weixinPaySignTool = paySign(appid, mchId,openid, outTradeNo, spbillCreateIp,
				Unifiedorder.TRADE_TYPE_JS, body, totalFee, notifyUrl, null, key);

		//return new JsPaySignTool(appid, weixinPaySignTool.getPackage_(), key);
		return new JsPaySignTool(weixinPaySignTool.getTimeStamp(), weixinPaySignTool.getNonceStr(),  weixinPaySignTool.getPaySign(), appid, weixinPaySignTool.getPackage_(), weixinPaySignTool.getSignType());
	}




	/**
	 *  微信支付退款，2018年2月7日，江雄新增
	 * @param appid
	 * @param mchId
	 * @param transactionId 微信支付流水号
	 * @param outRefundNo。内部退款编号
	 * @param totalFee 总金额。分
	 * @param refundFee 退款金额。分
	 */
	public static Map<String,String> Refund(String appid, String mchId, String transactionId, String outRefundNo, Integer totalFee, Integer refundFee,String key) throws  Exception{
		Map<String,String> result = new HashMap<String,String>();


		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		FileInputStream instream = new FileInputStream(new File("/summer/project/fireadmin/static/apiclient_cert.p12"));
		try {
			keyStore.load(instream, mchId.toCharArray());
		}finally {
			instream.close();
		}
		// Trust own CA and all self-signed certs
		SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
		// Allow TLSv1 protocol only
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
				sslcontext, new String[] { "TLSv1" }, null,
				SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
		CloseableHttpClient httpclient = HttpClients.custom()
				.setSSLSocketFactory(sslsf).build();

		Refundorder refundorder = new Refundorder(appid,mchId, transactionId, outRefundNo, totalFee, refundFee,key);
		String xml = refundorder.getXML();//获得退款单的xml参数'
		logger.info("--------微信退款XML------------"+xml);
		HttpPost httppost = new HttpPost(Refundorder.URL);
		try {
			StringEntity se = new StringEntity(xml);
			httppost.setEntity(se);
			//System.out.println("executing request" + httppost.getRequestLine());
			CloseableHttpResponse responseEntry = httpclient.execute(httppost);
			try {
				//4.获取响应的实体内容，就是我们所要抓取得网页内容
				HttpEntity entity = responseEntry.getEntity();
				logger.info("微信退款状态码======="+responseEntry.getStatusLine().getStatusCode());
				if (entity != null) {
					//logger.info("微信退款相应内容--"+ EntityUtils.toString(entity, "utf-8"));
					//logger.info("微信退款相应内容-长度--"+ entity.getContentLength());

					SAXReader saxReader = new SAXReader();
					Document document = saxReader.read(entity.getContent());
					Element rootElt = document.getRootElement();
					logger.info("微信退款相应内容-根节点：" + rootElt.getName());
					logger.info("微信退款相应内容=result_code="+rootElt.elementText("result_code"));
					logger.info("微信退款相应内容=return_msg="+rootElt.elementText("return_msg"));
					logger.info("微信退款相应内容=out_refund_no="+rootElt.elementText("out_refund_no"));
					logger.info("微信退款相应内容=refund_fee="+rootElt.elementText("refund_fee"));
					result.put("result_code",rootElt.elementText("result_code"));
					result.put("out_refund_no",rootElt.elementText("out_refund_no"));
					result.put("refund_fee",rootElt.elementText("refund_fee"));
					//String resultCode = rootElt.elementText("result_code");

					//String resultCode = rootElt.elementText("result_code");
					/*JSONObject result = new JSONObject();

					Document documentXml = DocumentHelper.parseText(xml);
					Element rootEltXml = documentXml.getRootElement();
					if(resultCode.equals("SUCCESS")){
						System.out.println("=================prepay_id===================="+ rootElt.elementText("prepay_id"));
						System.out.println("=================sign===================="+ rootEltXml.elementText("sign"));
						result.put("weixinPayUrl", rootElt.elementText("code_url"));
						result.put("prepayId", rootElt.elementText("prepay_id"));
						result.put("status","success");
						result.put("msg","success");
					}else{
						result.put("status","false");
						result.put("msg",rootElt.elementText("err_code_des"));
					}
					return result;*/
				}
				//EntityUtils.consume(entity);
			}catch (Exception e){
				e.printStackTrace();
				logger.info("微信退款--------------"+e.getMessage());
			}finally {
				responseEntry.close();
			}
		}catch (Exception e){
			e.printStackTrace();
			logger.info("微信退款2222222222--------------"+e.getMessage());
		}finally {
			httpclient.close();
		}
		return result;
	}


	/**
	 * 企业付款
	 * @param mchAppId appId
	 * @param mchId 商户ID
	 * @param partnerTradeNo 内部订单号
	 * @param openId
	 * @param checkName 是否校验收款人真实姓名 NO_CHECK：不校验真实姓名；FORCE_CHECK：强校验真实姓名
	 * @param reUserName 收款人真实姓名
	 * @param amount 金额
	 * @param desc 付款描述
	 * @param spbillCreateIp 服务器IP
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static Map<String,String> companyPay(
			String mchAppId, String mchId,
			String partnerTradeNo,
			String openId, String checkName, String reUserName, Integer amount, String desc, String spbillCreateIp, String key)

			throws  Exception{
		//企业付款实体类

		Map<String,String> result = new HashMap<String,String>();

		//加载证书
		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		FileInputStream instream = new FileInputStream(new File("/fire/project/summeradmin/static/apiclient_cert.p12"));
		try {
			keyStore.load(instream, mchId.toCharArray());
		}finally {
			instream.close();
		}
		// Trust own CA and all self-signed certs
		SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
		// Allow TLSv1 protocol only
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
				sslcontext, new String[] { "TLSv1" }, null,
				SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
		CloseableHttpClient httpclient = HttpClients.custom()
				.setSSLSocketFactory(sslsf).build();
		//企业付款实体类
		Transfers transfers = new Transfers(mchAppId,mchId,
				partnerTradeNo,
				openId, checkName, reUserName,amount, desc,spbillCreateIp,key);

		String xml = transfers.getXML();//获得退款单的xml参数'
		logger.info("--------微信企业付款XML------------"+xml);
		HttpPost httppost = new HttpPost(Transfers.URL);
		try {
			//注意处理编码
			StringEntity se = new StringEntity(xml,"utf-8");
			httppost.setEntity(se);
			//System.out.println("executing request" + httppost.getRequestLine());
			CloseableHttpResponse responseEntry = httpclient.execute(httppost);
			try {
				//4.获取响应的实体内容，就是我们所要抓取得网页内容
				HttpEntity entity = responseEntry.getEntity();
				logger.info("微信退款状态码======="+responseEntry.getStatusLine().getStatusCode());
				if (entity != null) {
					//logger.info("微信退款相应内容--"+ EntityUtils.toString(entity, "utf-8"));
					//logger.info("微信退款相应内容-长度--"+ entity.getContentLength());

					SAXReader saxReader = new SAXReader();
					Document document = saxReader.read(entity.getContent());
					Element rootElt = document.getRootElement();

					logger.info("微信退款相应内容-根节点：" + rootElt.getName());
					logger.info("微信退款相应内容=return_code="+rootElt.elementText("return_code"));
					logger.info("微信退款相应内容=return_msg="+rootElt.elementText("return_msg"));
					logger.info("微信退款相应内容=result_code="+rootElt.elementText("result_code"));
					logger.info("微信退款相应内容=payment_no="+rootElt.elementText("payment_no"));
					logger.info("微信退款相应内容=payment_time="+rootElt.elementText("payment_time"));
					logger.info("微信退款相应内容=err_code="+rootElt.elementText("err_code"));
					logger.info("微信退款相应内容=err_code_des="+rootElt.elementText("err_code_des"));
					//SUCCESS/FAIL此字段是通信标识，非交易标识，交易是否成功需要查看result_code来判断
					result.put("return_code",rootElt.elementText("return_code"));
					//返回信息，如非空，为错误原因签名失败参数格式校验错误
					result.put("return_msg",rootElt.elementText("return_msg"));
					//业务结果，判断交易是否成功
					result.put("result_code",rootElt.elementText("result_code"));
					//微信支付流水号
					result.put("payment_no",rootElt.elementText("payment_no"));
					//微信支付流水时间
					result.put("payment_time",rootElt.elementText("payment_time"));
					//错误码。
					//重要--------重要--重要---
					/**
					 *  如果是  SYSTEMERROR
					 * 请使用原单号以及原请求参数重试，否则可能造成重复支付等资金风险
					 */
					result.put("err_code",rootElt.elementText("err_code"));
					//错误码，描述
					result.put("err_code_des",rootElt.elementText("err_code_des"));






					/*Document documentXml = DocumentHelper.parseText(xml);
					Element rootEltXml = documentXml.getRootElement();
					if(resultCode.equals("SUCCESS")){
						System.out.println("=================prepay_id===================="+ rootElt.elementText("prepay_id"));
						System.out.println("=================sign===================="+ rootEltXml.elementText("sign"));
						result.put("weixinPayUrl", rootElt.elementText("code_url"));
						result.put("prepayId", rootElt.elementText("prepay_id"));
						result.put("status","success");
						result.put("msg","success");
					}else{
						result.put("status","false");
						result.put("msg",rootElt.elementText("err_code_des"));
					}*/
					return result;
				}
				//EntityUtils.consume(entity);
			}catch (Exception e){
				e.printStackTrace();
				logger.info("微信退款--------------"+e.getMessage());
			}finally {
				responseEntry.close();
			}
		}catch (Exception e){
			e.printStackTrace();
			logger.info("微信退款2222222222--------------"+e.getMessage());
		}finally {
			httpclient.close();
		}
		return result;
	}


}
